package com.ljx.funchat.user.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.ljx.funchat.user.dao.UserDao;
import com.ljx.funchat.user.domain.entity.User;
import com.ljx.funchat.user.service.UserService;
import com.ljx.funchat.user.service.adatper.TextBuilder;
import com.ljx.funchat.user.service.adatper.UserAdapter;
import com.ljx.funchat.websocket.service.WebSocketService;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import java.net.URLDecoder;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Description: 处理与微信api的交互逻辑
 * Author: <a href="https://github.com/zongzibinbin">abin</a>
 * Date: 2023-03-19
 */
@Service
@Slf4j
public class WxMsgService {
    /**
     * 微信授权地址
     */
    private static final String URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";

    /**
     * 用户的openId和前端登录场景code的映射关系
     */
    private static final ConcurrentHashMap<String, Integer> WAIT_AUTHORIZE_MAP = new ConcurrentHashMap<>();


    @Value("${wx.mp.callback}")
    private String callback;

    @Autowired
    private UserDao userDao;

    @Autowired
    private UserService userService;

    @Autowired
    private WebSocketService webSocketService;

    public WxMpXmlOutMessage scan(WxMpService wxMpService, WxMpXmlMessage wxMpXmlMessage) {
        String openid = wxMpXmlMessage.getFromUser();
        Integer code = getEventKey(wxMpXmlMessage);
        if (ObjectUtil.isNull(code)){
            return null;
        }

        // 保存用户信息
        User user = userDao.getByOpenId(openid);
        boolean registered = Objects.nonNull(user);
        boolean authorized = registered && StrUtil.isNotBlank(user.getName());
        if (registered && authorized){
            // 用户已经注册了，直接返回用户+token信息
            webSocketService.scanLoginSuccess(code, user.getId());
            return null;
        }
        if (!registered){
            // 未写入数据库
            User insert = UserAdapter.buildUserSave(openid);
            userService.register(insert);
        }
        WAIT_AUTHORIZE_MAP.put(openid, code);
        // 通知前端正在等待认证
        webSocketService.waitAuthorize(code);
        // 拼接URL，获取认证地址
        String authorizeUrl = String.format(URL, wxMpService.getWxMpConfigStorage().getAppId(), URLDecoder.decode(callback + "wx/portal/public/callBack"));
        return TextBuilder.build("请点击登录：<a href=\"" + authorizeUrl + "\">登录</a>", wxMpXmlMessage, wxMpService);
    }

    /**
     * 获取登录事件码
     * @param wxMpXmlMessage
     * @return
     */
    private Integer getEventKey(WxMpXmlMessage wxMpXmlMessage) {
        // 扫码关注的渠道事件有前缀，需要去除
        try {
            String code = wxMpXmlMessage.getEventKey().replace("qrscene_", "");
            return Integer.parseInt(code);
        } catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 用户授权
     * @param userInfo
     */
    public void authorize(WxOAuth2UserInfo userInfo) {
        String openid = userInfo.getOpenid();
        User user = userDao.getByOpenId(openid);
        if (StrUtil.isEmpty(user.getName())){
            // 第一次登录，更新用户信息
            fillUserInfo(user.getId(), userInfo);
        }
        // 通过code找到用户channel进行登录
        Integer code = WAIT_AUTHORIZE_MAP.remove(openid);
        webSocketService.scanLoginSuccess(code, user.getId());
    }

    /**
     * 填充用户信息【第一次登录信息不全】
     * @param uid
     * @param userInfo
     */
    private void fillUserInfo(Long uid, WxOAuth2UserInfo userInfo) {
        User user = UserAdapter.buildAuthorUser(uid, userInfo);
        // 查询是否名字冲突
        try {
            if (userDao.countByName(user.getName()) > 0){
                return;
            }
        } catch (DuplicateKeyException e){
            user.setName("重复昵称" + RandomUtil.randomString(6));
            log.info("名字重复");
        }
        // 更新信息
        userDao.updateById(user);
    }
}